#include "stdafx.h"
#include <windows.h>
#include <pimstore.h>
#include "ListDataCallLog.h"

#include "PhoneUtils.h"
#include "Settings.h"
#include "Language.h"
#include "Theme.h"
#include "PoomData.h"
#include "EdbData.h"
#include "CommonWM.h"
#include "ListDetails.h"

#ifdef _DEBUG
#include "crtdbg.h"
#endif

// clog.db entry contains:
// 0  I2  ? always 1
// 1  FT  startcall
// 2  FT  endcall
// 3  I4  low byte is calltype [4=missed, 5=outgoing, 6=incoming, 7=rejected]
// 4  STR number
// 5  STR last, firstname
// 6  STR numbertype
// 7      ?
#define CLOG_NUM_PROPS        7
#define CLOG_PROP_STARTTIME   1
#define CLOG_PROP_ENDTIME     2
#define CLOG_PROP_CALLTYPE    3
#define CLOG_PROP_NUMBER      4
#define CLOG_PROP_NAME        5
#define CLOG_PROP_NUMBERTYPE  6

#define CLOG_CALLTYPE_OUTGOING  0x01  // .1 indicates outgoing, .0 indicates incoming
#define CLOG_CALLTYPE_CONNECTED 0x02  // 1. indicates accepted call, 0. indicates missed/rejected call

#define CLOG_PROP_STARTTIME_OID MAKELONG(CEVT_FILETIME, 2)

CallLogEntry::CallLogEntry()
: oId(0), numberType(iptTelephoneNumber), callType(ctAll)
, cPastTime(0), numRepeats(0)
{
  szName[0] = 0;
  szNumber[0] = 0;
  ZeroMemory(&ftStart, sizeof(ftStart));
  ZeroMemory(&ftEnd, sizeof(ftEnd));
  szPastTime[0] = 0;
  szRepeatingInfo[0] = 0;
}

bool CallLogEntry::isRepeating(const CallLogEntry& b) const
{
  return (this->callType  == b.callType &&
    this->cPastTime == b.cPastTime &&
    ((this->hasName() && b.hasName() &&
    this->hasNumber() && b.hasNumber() &&
    wcscmp(this->szName, b.szName) == 0 &&
    wcscmp(this->szNumber, b.szNumber) == 0)
    || (!this->hasName() && !b.hasName() &&
    this->hasNumber() && b.hasNumber() &&
    wcscmp(this->szNumber, b.szNumber) == 0)));
}


ListDataCallLog::ListDataCallLog(HWND hWnd, CallType forType)
: CListBase(hWnd)
{
  this->callType = forType;

  if (Settings::readHistoryFromDB)
  {
    SORTORDERSPECEX sortOrder = {0};
    sortOrder.wVersion = 2;
    sortOrder.wNumProps = 1;
    sortOrder.rgPropID[0] = CLOG_PROP_STARTTIME_OID;
    sortOrder.rgdwFlags[0] = CEDB_SORT_DESCENDING;
    if (!EdbData::OpenDatabase(EDB_CLOG, &sortOrder, true))
    {
      throw TEXT("Unable to open CallLog database!");
    }

    this->PopulateCListByDB();
  }
  else
  {
    // to receive notifications
    EdbData::OpenDatabase(EDB_CLOG, true);
    this->PopulateCListByPhone();
  }

  this->Populate();
}

ListDataCallLog::~ListDataCallLog()
{
  ClearList(listMissed);
  ClearList(listIncoming);
  ClearList(listOutgoing);

  EdbData::CloseDatabase(EDB_CLOG);
}

void ListDataCallLog::Clear()
{
  ClearList(listMissed);
  ClearList(listIncoming);
  ClearList(listOutgoing);

  CListBase::Clear();
}

bool ListDataCallLog::AddListEntry(CallLogEntry* pEntry)
{
  if (!pEntry)
    return false;

  switch(pEntry->callType)
  {
  case ctMissed:
    listMissed.push_back(pEntry);
    break;
  case ctIncoming:
    listIncoming.push_back(pEntry);
    break;
  case ctOutgoing:
    listOutgoing.push_back(pEntry);
    break;
  default:
    return false;
  }

  return true;
}

void ListDataCallLog::ClearList(CCallLogEntryList& list)
{
  for (CCallLogEntryList::iterator iter = list.begin(); iter != list.end(); iter++)
  {
    if (*iter != NULL)
      delete (*iter);
  }
  list.clear();
}

bool ListDataCallLog::PopulateCListByDB()
{
  DWORD numDbEntries = EdbData::GetNumRecords(EDB_CLOG);
  if (numDbEntries == 0)
  {
    return true;
  }

  // seek database to the beginning
  EdbData::SeekDatabase(EDB_CLOG, CEDB_SEEK_BEGINNING, 0);

  // prepare limit date
  FILETIME ftLimit;
  if (Settings::limitHistory > 0)
  {
    FILETIME ft;
    SYSTEMTIME st;
    ULARGE_INTEGER ulLimit;
    GetLocalTime(&st);
    st.wMinute = 0;
    st.wSecond = 0;
    st.wHour = 0;
    SystemTimeToFileTime(&st, &ft);
    LocalFileTimeToFileTime(&ft, &ftLimit);
    ulLimit.HighPart = ftLimit.dwHighDateTime;
    ulLimit.LowPart = ftLimit.dwLowDateTime;
    ulLimit.QuadPart -= (ULONGLONG)Settings::limitHistory * FILETIME_ONE_DAY;
    ftLimit.dwHighDateTime = ulLimit.HighPart;
    ftLimit.dwLowDateTime = ulLimit.LowPart;
  }

  CallLogEntry *lastMissed = NULL;
  CallLogEntry *lastIncoming = NULL;
  CallLogEntry *lastOutgoing = NULL;
  TCHAR szTime[SECONDARY_TEXT_LENGTH];

  PropertyValues vals;
  WORD       nPropCount;
  DWORD      dwVal;
  CEOID      oId;
  for (DWORD i = 0; i < numDbEntries; i++)
  {
    oId = EdbData::ReadRecord(EDB_CLOG, vals, nPropCount);
    if (oId == 0)
      break;

    if (nPropCount < CLOG_NUM_PROPS)
      break;

    CallLogEntry* cEntry = new CallLogEntry();
    if (!cEntry)
      break;

    // get call type
    if (EdbData::GetPropertyValue(vals[CLOG_PROP_CALLTYPE], dwVal))
    {
      if (dwVal & CLOG_CALLTYPE_OUTGOING)
        cEntry->callType = ctOutgoing;
      else if (dwVal & CLOG_CALLTYPE_CONNECTED)
        cEntry->callType = ctIncoming;
      else
        cEntry->callType = ctMissed;
    }

    // get start time
    EdbData::GetPropertyValue(vals[CLOG_PROP_STARTTIME], cEntry->ftStart);

    if (Settings::limitHistory > 0)
    {
      if (CompareFileTime(&cEntry->ftStart, &ftLimit) < 0)
      { // stop if entry is too old
        delete cEntry;
        break;
      }
    }

    cEntry->oId = oId;
    cEntry->cPastTime = _getPastTime(cEntry->ftStart, cEntry->szPastTime, SECONDARY_TEXT_LENGTH);

    int iSamsung=0; // Work around for certain Samsung call history databases -- by [kab]

    if (!EdbData::IsPropertyEmpty(vals[CLOG_PROP_NAME]))
    {
      if (Settings::samsungHistoryWorkaround)
      {
        iSamsung=1;
      }
      EdbData::GetPropertyValue(vals[CLOG_PROP_NAME+iSamsung], cEntry->szName, PRIMARY_TEXT_LENGTH);
      if (cEntry->szName[0] == TEXT('0') || cEntry->szName[0] == 0)
      {
        cEntry->szName[0] = 0;
        iSamsung=0;
      }
      EdbData::GetPropertyValue(vals[CLOG_PROP_NUMBER+iSamsung], cEntry->szNumber, PRIMARY_TEXT_LENGTH);
    }
    else if (!EdbData::IsPropertyEmpty(vals[CLOG_PROP_NUMBER]))
    {
      if (!Settings::showDialingNumber)
      {
        EdbData::GetPropertyValue(vals[CLOG_PROP_NUMBER], cEntry->szName, PRIMARY_TEXT_LENGTH);
      }
      EdbData::GetPropertyValue(vals[CLOG_PROP_NUMBER], cEntry->szNumber, PRIMARY_TEXT_LENGTH);
    }

    if (!EdbData::IsPropertyEmpty(vals[CLOG_PROP_NUMBERTYPE+iSamsung]))
    {
      TCHAR numType[16];
      if (EdbData::GetPropertyValue(vals[CLOG_PROP_NUMBERTYPE+iSamsung], numType, 16))
      {
        cEntry->numberType = ParseNumberType(numType);
      }
    }

    EdbData::GetPropertyValue(vals[CLOG_PROP_ENDTIME], cEntry->ftEnd);

    if (Settings::collapseRepeatsInHistory)
    {
      CallLogEntry* lastEntry = NULL;
      switch(cEntry->callType)
      {
      case ctMissed:   lastEntry = lastMissed;   break;
      case ctIncoming: lastEntry = lastIncoming; break;
      case ctOutgoing: lastEntry = lastOutgoing; break;
      }
      if (lastEntry != NULL && cEntry->isRepeating(*lastEntry))
      {
        // this is a repeat, increase counter
        lastEntry->numRepeats++;
        if (lastEntry->numRepeats > 1)
          StringCchCat(lastEntry->szRepeatingInfo, PRIMARY_TEXT_LENGTH*2, TEXT(", "));
        _printTime(szTime, SECONDARY_TEXT_LENGTH, cEntry->ftStart);
        StringCchCat(lastEntry->szRepeatingInfo, PRIMARY_TEXT_LENGTH*2, szTime);
        delete cEntry;
      }
      else
      {
        AddListEntry(cEntry);
        switch(cEntry->callType)
        {
        case ctMissed:   lastMissed = cEntry;   break;
        case ctIncoming: lastIncoming = cEntry; break;
        case ctOutgoing: lastOutgoing = cEntry; break;
        }
      }
    }
    else
    {
      AddListEntry(cEntry);
    }

    ::SendMessage(parentWnd, NM_UPDATE_BUSY_PROGRESS, 0, (LPARAM)(((i+1) * 100) / numDbEntries));
  } // for

  vals.Free();

  return (numDbEntries > 0);
}

void ListDataCallLog::PopulateCListByPhone()
{
  HANDLE ph;
  DWORD lastEntryIndex;
  DWORD currentEntryIndex;

  Clear();

  if (FAILED(PhoneOpenCallLog(&ph)))
    return;

  if (FAILED(PhoneSeekCallLog(ph, CALLLOGSEEK_END, 0, &lastEntryIndex)))
  {
    PhoneCloseCallLog(ph);
    return;
  }

  if (FAILED(PhoneSeekCallLog(ph, CALLLOGSEEK_BEGINNING, 0, &currentEntryIndex)))
  {
    PhoneCloseCallLog(ph);
    return;
  }

  // prepare limit date
  FILETIME ftLimit;
  if (Settings::limitHistory > 0)
  {
    FILETIME ft;
    SYSTEMTIME st;
    ULARGE_INTEGER ulLimit;
    GetLocalTime(&st);
    st.wMinute = 0;
    st.wSecond = 0;
    st.wHour = 0;
    SystemTimeToFileTime(&st, &ft);
    LocalFileTimeToFileTime(&ft, &ftLimit);
    ulLimit.HighPart = ftLimit.dwHighDateTime;
    ulLimit.LowPart = ftLimit.dwLowDateTime;
    ulLimit.QuadPart -= (ULONGLONG)Settings::limitHistory * FILETIME_ONE_DAY;
    ftLimit.dwHighDateTime = ulLimit.HighPart;
    ftLimit.dwLowDateTime = ulLimit.LowPart;
  }

  CALLLOGENTRY clEntry;
  ::ZeroMemory(&clEntry, sizeof(CALLLOGENTRY));
  //cbSize MUST be set before passing the struct in to the function!
  // Refer to the doc.
  clEntry.cbSize = sizeof(CALLLOGENTRY);

  CallLogEntry *lastMissed = NULL;
  CallLogEntry *lastIncoming = NULL;
  CallLogEntry *lastOutgoing = NULL;
  TCHAR szTime[SECONDARY_TEXT_LENGTH];

  for (int i = (int)currentEntryIndex; i <= (int)lastEntryIndex; i++)
  {
    clEntry.pszNumber = NULL;
    clEntry.pszName = NULL;
    clEntry.pszNameType = NULL;
    clEntry.pszNote = NULL;

    if (FAILED(PhoneGetCallLogEntry(ph, &clEntry)))
      break;

    CallLogEntry* cEntry = new CallLogEntry();
    if (!cEntry)
      break;

    switch(clEntry.iom)
    {
    case IOM_INCOMING:
      cEntry->callType = ctIncoming;
      break;
    case IOM_OUTGOING:
      cEntry->callType = ctOutgoing;
      break;
    case IOM_MISSED:
      cEntry->callType = ctMissed;
      break;
    }

    if (Settings::limitHistory > 0)
    {
      if (CompareFileTime(&clEntry.ftStartTime, &ftLimit) < 0)
      {
        // entry older than limit, break here
        if (clEntry.pszNumber) LocalFree(clEntry.pszNumber);
        if (clEntry.pszName) LocalFree(clEntry.pszName);
        if (clEntry.pszNameType) LocalFree(clEntry.pszNameType);
        if (clEntry.pszNote) LocalFree(clEntry.pszNote);
        delete cEntry;
        break;
      }
    }

    cEntry->cPastTime = _getPastTime(clEntry.ftStartTime,
      cEntry->szPastTime, SECONDARY_TEXT_LENGTH);

    if (clEntry.pszName)
    {
      StringCchCopy(cEntry->szName, PRIMARY_TEXT_LENGTH, clEntry.pszName);
      if (clEntry.pszNumber)
        StringCchCopy(cEntry->szNumber, PRIMARY_TEXT_LENGTH, clEntry.pszNumber);
    }
    else if (clEntry.pszNumber)
    {
      if (!Settings::showDialingNumber)
        StringCchCopy(cEntry->szName, PRIMARY_TEXT_LENGTH, clEntry.pszNumber);
      StringCchCopy(cEntry->szNumber, PRIMARY_TEXT_LENGTH, clEntry.pszNumber);
    }

    if (clEntry.pszNameType)
    {
      cEntry->numberType = ParseNumberType(clEntry.pszNameType);
    }

    CopyMemory(&cEntry->ftStart, &clEntry.ftStartTime, sizeof(FILETIME));
    CopyMemory(&cEntry->ftEnd, &clEntry.ftEndTime, sizeof(FILETIME));

    if (Settings::collapseRepeatsInHistory)
    {
      CallLogEntry* lastEntry = NULL;
      switch(cEntry->callType)
      {
      case ctMissed:   lastEntry = lastMissed;   break;
      case ctIncoming: lastEntry = lastIncoming; break;
      case ctOutgoing: lastEntry = lastOutgoing; break;
      }
      if (lastEntry != NULL && cEntry->isRepeating(*lastEntry))
      {
        // this is a repeat, increase counter
        lastEntry->numRepeats++;
        if (lastEntry->numRepeats > 1)
          StringCchCat(lastEntry->szRepeatingInfo, PRIMARY_TEXT_LENGTH*2, TEXT(", "));
        _printTime(szTime, SECONDARY_TEXT_LENGTH, cEntry->ftStart);
        StringCchCat(lastEntry->szRepeatingInfo, PRIMARY_TEXT_LENGTH*2, szTime);
        delete cEntry;
      }
      else
      {
        AddListEntry(cEntry);
        switch(cEntry->callType)
        {
        case ctMissed:   lastMissed = cEntry;   break;
        case ctIncoming: lastIncoming = cEntry; break;
        case ctOutgoing: lastOutgoing = cEntry; break;
        }
      }
    }
    else
    {
      AddListEntry(cEntry);
    }

    // cleanup
    if (clEntry.pszNumber) LocalFree(clEntry.pszNumber);
    if (clEntry.pszName) LocalFree(clEntry.pszName);
    if (clEntry.pszNameType) LocalFree(clEntry.pszNameType);
    if (clEntry.pszNote) LocalFree(clEntry.pszNote);

    ::SendMessage(parentWnd, NM_UPDATE_BUSY_PROGRESS, 0, (LPARAM)(((i+1) * 100) / (lastEntryIndex - currentEntryIndex + 1)));
  }

  PhoneCloseCallLog(ph);

  return;
}

ItemPropertyType ListDataCallLog::ParseNumberType(const TCHAR* type)
{
  if (_tcscmp(type, TEXT("m")) == 0)
    return iptMobileNumber;
  else if (_tcscmp(type, TEXT("w")) == 0)
    return iptBusinessNumber;
  else if (_tcscmp(type, TEXT("h")) == 0)
    return iptHomeNumber;
  else if (_tcscmp(type, TEXT("w2")) == 0)
    return iptBusinessNumber2;
  else if (_tcscmp(type, TEXT("h2")) == 0)
    return iptHomeNumber2;
  else if (_tcscmp(type, TEXT("car")) == 0)
    return iptCarNumber;
  else if (_tcscmp(type, TEXT("pgr")) == 0)
    return iptPager;
  else if (_tcscmp(type, TEXT("rdo")) == 0)
    return iptRadioNumber;
  else
    return iptTelephoneNumber;
}

void ListDataCallLog::Populate()
{
  bool foundname = false;
  int numItems = 0;
  switch(this->callType)
  {
  case ctAll:
    numItems = listMissed.size() + listIncoming.size() + listOutgoing.size();
    ClearHeaderText();
    break;
  case ctMissed:
    numItems = listMissed.size();
    SetHeaderText(Language::missedhdr);
    break;
  case ctIncoming:
    numItems = listIncoming.size();
    SetHeaderText(Language::incominghdr);
    break;
  case ctOutgoing:
    numItems = listOutgoing.size();
    SetHeaderText(Language::outgoinghdr);
    break;
  }
  if (numItems == 0)
    return;

  CCallLogEntryList::iterator iterMissed = listMissed.begin();
  CCallLogEntryList::iterator iterIncoming = listIncoming.begin();
  CCallLogEntryList::iterator iterOutgoing = listOutgoing.begin();
  for (int i = 0; i <= numItems; i++)
  {
    // find the list to get next entry from
    CallType lt = ctNone;
    if (this->callType == ctAll)
    {
      FILETIME ft = {0};
      if (iterMissed != listMissed.end() &&
        ::CompareFileTime(&((*iterMissed)->ftStart), &ft) > 0)
      {
        lt = ctMissed;
        ft = (*iterMissed)->ftStart;
      }
      if (iterIncoming != listIncoming.end() &&
        ::CompareFileTime(&((*iterIncoming)->ftStart), &ft) > 0)
      {
        lt = ctIncoming;
        ft = (*iterIncoming)->ftStart;
      }
      if (iterOutgoing != listOutgoing.end() &&
        ::CompareFileTime(&((*iterOutgoing)->ftStart), &ft) > 0)
      {
        lt = ctOutgoing;
        ft = (*iterOutgoing)->ftStart;
      }
    }
    else
    {
      lt = this->callType;
    }
    // apply the entry
    CallLogEntry* cEntry = NULL;
    switch(lt)
    {
    case ctMissed:
      if (iterMissed != listMissed.end())
      {
        cEntry = *iterMissed;
        iterMissed++;
      }
      break;
    case ctIncoming:
      if (iterIncoming != listIncoming.end())
      {
        cEntry = *iterIncoming;
        iterIncoming++;
      }
      break;
    case ctOutgoing:
      if (iterOutgoing != listOutgoing.end())
      {
        cEntry = *iterOutgoing;
        iterOutgoing++;
      }
      break;
    }
    if (cEntry == NULL)
      break;

    CListItemCallLog* pNewItem = new CListItemCallLog();
    if (!pNewItem)
      break;

    pNewItem->cEntry = cEntry;

    pNewItem->oId = pNewItem->FindOid(parentWnd, cEntry->szName, cEntry->szNumber, true);

    foundname = false;
    if (pNewItem->oId != 0)
    {
      // name from POOM
      TCHAR name[PRIMARY_TEXT_LENGTH] = {0};
      foundname = pNewItem->GetNameFromNumber(parentWnd, name, cEntry->szNumber, true);
      if (foundname)
      {
        pNewItem->SetPrimaryText(name);
      }

      // picture
      if (Settings::showPicturesInCallHistory)
      {
        pNewItem->picture = PoomPictures::Add(pNewItem->oId, Theme::ListPictureWidth, Theme::ListPictureHeight);
      }
    }

    // name, number, time, group from history
    if (!foundname)
    {
      if (cEntry->hasName())
        pNewItem->SetPrimaryText(cEntry->szName);
      else if (cEntry->hasNumber())
        pNewItem->SetPrimaryText(cEntry->szNumber);
      else
        pNewItem->SetPrimaryText(Language::unknown);
    }

    pNewItem->SetDialingNo(cEntry->szNumber, _T(""));
    pNewItem->SetSecondaryText(cEntry->szPastTime);
    pNewItem->wcGroup = cEntry->cPastTime;

    // num repeats
    if (cEntry->numRepeats > 0)
    {
      if (Settings::showRepeatsInName)
      {
        TCHAR fmt[12];
        StringCchPrintf(fmt, 12, TEXT(" (%d)"), cEntry->numRepeats + 1);
        StringCchCat(pNewItem->szPrimaryText, PRIMARY_TEXT_LENGTH, fmt);
        pNewItem->nPrimaryTextLength = _tcslen(pNewItem->szPrimaryText);
      }
      else
      {
        StringCchPrintf(pNewItem->szSecondaryText2, SECONDARY_TEXT_LENGTH, TEXT("[%d]"), cEntry->numRepeats + 1);
        pNewItem->nSecondaryText2Length = _tcslen(pNewItem->szSecondaryText2);
      }
    }
    Add(pNewItem);
  }

  return;
}

void ListDataCallLog::Reload(CallType forType, bool fullReload)
{
  this->callType = forType;

  if (fullReload)
  {
    ListDataCallLog::Clear();
    if (Settings::readHistoryFromDB)
      this->PopulateCListByDB();
    else
      this->PopulateCListByPhone();
  }
  else
  {
    CListBase::Clear();
  }

  this->Populate();
}

bool ListDataCallLog::OutlookContactChanged(CEOID oId)
{
  bool  isOIdFound = false;
  CEOID oIdFound = 0;
  size_t n = this->items.size();
  for (size_t i = 0; i < n; i++)
  {
    if (BaseItem(i)->oId == oId)
    {
      CallLogEntry* cEntry = ((CListItemCallLog*)items[i])->cEntry;
      if (cEntry != NULL)
      {
        if (!isOIdFound)
        {
          oIdFound = BaseItem(i)->FindOid(parentWnd, cEntry->szName, cEntry->szNumber, true);
          isOIdFound = true;
        }
        BaseItem(i)->oId = oIdFound;
      }
    }
  }

  return isOIdFound;
}

TCHAR ListDataCallLog::_getPastTime(FILETIME ftTime, TCHAR * pszSecondary, int strLength)
{
  // if ftTime somehow == 0, then get outta here
  if (!ftTime.dwHighDateTime && !ftTime.dwLowDateTime)
  {
    StringCchPrintf(pszSecondary, strLength, TEXT(""));
    return 0;
  }

  FILETIME ftToday;    // midnight today (but UTC)
  FILETIME ftYesterday;// midnight yesterday (but UTC)
  FILETIME ftThisWeek; // midnight one week ago (but UTC)

  // temporary vars
  SYSTEMTIME st;
  FILETIME ft;

  // calculate ftToday
  GetLocalTime(&st);
  st.wMinute = 0;
  st.wSecond = 0;
  st.wHour = 0;
  SystemTimeToFileTime(&st, &ft);
  LocalFileTimeToFileTime(&ft, &ftToday);
  if (CompareFileTime(&ftTime, &ftToday) >= 0)
  {
    FileTimeToLocalFileTime(&ftTime, &ft);
    FileTimeToSystemTime(&ft, &st);
    GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszSecondary, strLength);
    return 'T';
  }

  // calculate ftYesterday
  ULARGE_INTEGER uli;
  uli.HighPart = ftToday.dwHighDateTime;
  uli.LowPart = ftToday.dwLowDateTime;
  uli.QuadPart -= FILETIME_ONE_DAY;
  ftYesterday.dwHighDateTime = uli.HighPart;
  ftYesterday.dwLowDateTime = uli.LowPart;
  if (CompareFileTime(&ftTime, &ftYesterday) >= 0)
  {
    FileTimeToLocalFileTime(&ftTime, &ft);
    FileTimeToSystemTime(&ft, &st);
    GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszSecondary, strLength);
    return 'Y';
  }

  // calculate ftThisWeek
  uli.QuadPart -= FILETIME_SIX_DAYS;
  ftThisWeek.dwHighDateTime = uli.HighPart;
  ftThisWeek.dwLowDateTime = uli.LowPart;
  if (CompareFileTime(&ftTime, &ftThisWeek) >= 0)
  {
    FileTimeToLocalFileTime(&ftTime, &ft);
    FileTimeToSystemTime(&ft, &st);
    GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszSecondary, strLength);
    return st.wDayOfWeek + 1;
  }

  // Older than one week ago, print date in just month-year format
  FileTimeToLocalFileTime(&ftTime, &ft);
  FileTimeToSystemTime(&ft, &st);
  // determine date order and format
  TCHAR tmp[10];
  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, tmp, 10);
  int dateType = _tcstol(tmp, NULL, 10);
  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, tmp, 10);
  if (dateType == 1) // Day - Month - Year
    StringCchPrintf(pszSecondary, strLength, TEXT("%u%s%02u"),
    st.wDay, tmp, st.wMonth);
  else
    StringCchPrintf(pszSecondary, strLength, TEXT("%u%s%02u"),
    st.wMonth, tmp, st.wDay);
  return 'O';
}

void ListDataCallLog::_printTime(TCHAR * str, int strLength, FILETIME ft)
{
  FILETIME ftLocal;
  SYSTEMTIME st;
  FileTimeToLocalFileTime(&ft, &ftLocal);
  FileTimeToSystemTime(&ftLocal, &st);
  GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, str, strLength);
}